# -*- coding: utf-8 -*-

#    OriginDB
#    Copyright (C) 2009  Jason Power <power.jg@gmail.com>
#    Copyright (C) 2010  Travis Gockel <travis@gockelhut.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import sys
sys.path.append('../')

from PyQt4 import QtCore, QtGui
from ui_compoundEdit import Ui_CompoundEditFrame

import editor
from common import loadFile

import pybel

class CompoundEdit(QtGui.QFrame):
	
	def __init__(self, controller, compoundClass, mode='New'):
		QtGui.QFrame.__init__(self)

		self.controller = controller

		self.compoundClass = compoundClass
		self.compound = None
		self.molfile = ""
		self.compoundItem = None

        # Set up the user interface from Designer.
		self.ui = Ui_CompoundEditFrame()
		self.ui.setupUi(self)

		self.setReadonly(not self.controller.userLoggedIn())

		self.setMode(mode)

		self.scene = QtGui.QGraphicsScene()
		self.ui.structureGraphicsView.setScene(self.scene)

		self.clear()

		self.connect(self.ui.viewStructurePushButton, QtCore.SIGNAL("clicked()"), self.controller.viewStructure)
		self.connect(self.ui.addCompoundPushButton, QtCore.SIGNAL("clicked()"), self.insertCompound)
		self.connect(self.ui.updatePushButton, QtCore.SIGNAL("clicked()"), self.updateCompoundAction)
		self.connect(self.ui.searchPushButton, QtCore.SIGNAL("clicked()"), self.search)
		self.connect(self.ui.productsPushButton, QtCore.SIGNAL("clicked()"), self.searchProducts)
		self.connect(self.ui.reactantsPushButton, QtCore.SIGNAL("clicked()"), self.searchReactants)
		self.connect(self.ui.subSearchPushButton, QtCore.SIGNAL("clicked()"), self.subSearch)
		self.connect(self.ui.browseStructurePushButton, QtCore.SIGNAL("clicked()"), self.browseStructure)
		self.connect(self.ui.importPushButton, QtCore.SIGNAL("clicked()"), self.controller.importCompoundAction)
		self.connect(self.ui.clearPushButton, QtCore.SIGNAL("clicked()"), self.clear)
		self.connect(self.ui.hydrogensPushButton, QtCore.SIGNAL("toggled(bool)"), self.showHydrogens)

		self.connect(self.ui.inchiLineEdit, QtCore.SIGNAL("editingFinished()"), self.inchiUpdate)


	def clear(self):
		self.ui.nameLineEdit.clear()
		self.ui.formulaLineEdit.clear()
		self.ui.IUPACLineEdit.clear()
		self.ui.inchiLineEdit.clear()

		self.scene.clear()

		self.ui.productsPushButton.setEnabled(False)
		self.ui.reactantsPushButton.setEnabled(False)

		self.compound = self.compoundClass()
		self.molfile = ""

	def initFocus(self):
		self.ui.nameLineEdit.setFocus(QtCore.Qt.OtherFocusReason)

	def getMol(self, structure=""):
		if not structure:
			structure = self.getStructureText()
		
		fmt = 'mol'
		s = structure
		if not s:
			fmt = 'inchi'
			s = str(self.ui.inchiLineEdit.text())
		if not s:
			return None

		mol = pybel.readstring(fmt, s)

		#This is a hack to see if the creation worked
		good = False
		for i in mol:
			good = True
			break

		if not good:
			self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Format Error", "The "+ fmt+ " text is badly formatted", QtGui.QMessageBox.Ok)
			self.msgDialog.show()
			return None
		else:
			return mol

	def setStructure(self, mol):

		if type(mol) == str:
			self.molfile = mol
			if not mol:
				return
				
			mol = self.getMol(mol)
		
		if not mol:
			return

		self.mol = mol
		self.mol.removeh()
		
		self.compoundItem = editor.Compound(mol)
		self.scene.clear()
		self.scene.addItem(self.compoundItem)

		inchi = mol.write("inchi")
		formula = mol.formula
		self.ui.inchiLineEdit.setText(inchi)
		self.ui.formulaLineEdit.setText(formula)

	def getStructureText(self):
		return self.molfile

	def insertCompound(self):
		if not len(str(self.ui.nameLineEdit.text())) > 0:
			self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Format Error", "No name provided", QtGui.QMessageBox.Ok)
			self.msgDialog.show()
			return

		mess = ''
		if not len(str(self.ui.formulaLineEdit.text())):
			mess += "No formula provided\n"
		if not len(str(self.ui.inchiLineEdit.text())):
			mess += "No InChI provided\n"

		if mess:
			self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Format Error", mess, QtGui.QMessageBox.Ignore)
			self.msgDialog.addButton(QtGui.QMessageBox.Cancel)
			ret = self.msgDialog.exec_()
			if ret == 4194304: #seems to be a magic number... bug in pyqt?
				return

		self.compound = self.compoundClass()

		if not self.updateCompound():
			return
		self.compound.insert()
		self.ui.formulaLineEdit.setText(self.compound.formula)

		self.ui.productsPushButton.setEnabled(True)
		self.ui.reactantsPushButton.setEnabled(True)

		self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Information, "Compound Added", "Compound added successfully", QtGui.QMessageBox.Ok)
		self.msgDialog.show()

	def updateCompound(self):
		self.compound.name = str(self.ui.nameLineEdit.text()).lower().strip()
		self.compound.IUPACname = str(self.ui.IUPACLineEdit.text()).lower().strip()
		self.compound.inchi = str(self.ui.inchiLineEdit.text()).strip("\n")

		formula = self.validateFormula(str(self.ui.formulaLineEdit.text()).strip())
		if not formula:
			self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Format Error", "Formula contains invalid elements or syntax error.", QtGui.QMessageBox.Ok)
			self.msgDialog.show()
			return False
		else:
			if type(formula) == bool:
				self.compound.formula = ""
			else:
				self.compound.formula = formula

		self.compound.molfile = self.molfile

		self.mol = self.getMol()

		if self.mol:
			print "calcing fingerprint:"
			print self.mol.atoms
			self.compound.fingerprint = self.mol.calcfp().bits

		return True

	def updateCompoundAction(self):
		if not self.updateCompound():
			return
		self.compound.forceUpdate()

		self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Information, "Compound Updated", "Compound updated successfully", QtGui.QMessageBox.Ok)
		self.msgDialog.show()

	def search(self):
		self.compound = self.compoundClass()
		self.compound.name = str(self.ui.nameLineEdit.text())
		self.compound.formula = str(self.ui.formulaLineEdit.text())
		self.compound.IUPACname = str(self.ui.IUPACLineEdit.text())
		self.compound.inchi = str(self.ui.inchiLineEdit.text())
		if self.mode != "Dialog":
			self.controller.searchCompound(self.compound)
		else:
			self.controller.searchCompoundFromDialog(self.compound)

	def setCurrentCompound(self, compound):
		self.compound = compound
		self.updateFields()

		if not self.readonly:
			self.ui.updatePushButton.show()

		self.ui.productsPushButton.setEnabled(True)
		self.ui.reactantsPushButton.setEnabled(True)

	def updateFields(self):
		self.setStructure(self.compound.molfile)

		if self.compound.name:
			self.ui.nameLineEdit.setText(self.compound.name)
		if self.compound.formula:
			self.ui.formulaLineEdit.setText(self.compound.formula)
		if self.compound.IUPACname:
			self.ui.IUPACLineEdit.setText(self.compound.IUPACname)
		if self.compound.inchi:
			self.ui.inchiLineEdit.setText(self.compound.inchi)

	def setReadonly(self, readonly):
		self.readonly = readonly

		#self.ui.nameLineEdit.setReadOnly(readonly)
		#self.ui.formulaLineEdit.setReadOnly(readonly)
		#self.ui.IUPACLineEdit.setReadOnly(readonly)
		#self.ui.inchiLineEdit.setReadOnly(readonly)
		#self.ui.structureTextEdit.setReadOnly(readonly)

		self.ui.addCompoundPushButton.setEnabled(not readonly)
		if not readonly and self.compound and self.compound.key:
			self.ui.updatePushButton.show()
		else:
			self.ui.updatePushButton.hide()

	def searchProducts(self):
		self.controller.searchProducts(self.compound)

	def searchReactants(self):
		self.controller.searchReactants(self.compound)

	def subSearch(self):
		mol = self.getMol(self.molfile)

		if mol:
			fp = mol.calcfp().bits
		else:
			self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Format Error", "Bad format for substructure search", QtGui.QMessageBox.Ok)
			self.msgDialog.show()
			return
		if self.mode != "Dialog":
			self.controller.subSearch(fp)
		else:
			self.controller.subSearchFromDialog(fp)

	def inchiUpdate(self):
		mol = self.getMol()
		self.setStructure(mol)

	def setMode(self, mode):
		self.mode = mode
		self.ui.searchPushButton.show()
		self.ui.updatePushButton.show()
		self.ui.addCompoundPushButton.show()
		self.ui.editLine.show()
		self.ui.productsPushButton.show()
		self.ui.reactantsPushButton.show()
		self.ui.nameLabel.show()
		self.ui.formulaLabel.show()
		self.ui.IUPACLabel.show()
		self.ui.inchiLabel.show()
		self.ui.nameLineEdit.show()
		self.ui.formulaLineEdit.show()
		self.ui.IUPACLineEdit.show()
		self.ui.inchiLineEdit.show()
		self.ui.searchLine.show()
		if mode == "New":
			self.ui.searchPushButton.hide()
			self.ui.updatePushButton.hide()
			self.ui.addCompoundPushButton.show()
			self.ui.editLine.hide()
			self.ui.subSearchPushButton.hide()
		elif mode == "Search":
			self.ui.searchPushButton.show()
			self.ui.updatePushButton.hide()
			self.ui.addCompoundPushButton.hide()
			self.ui.editLine.hide()
			self.ui.subSearchPushButton.hide()
		elif mode == "Dialog":
			self.ui.searchPushButton.show()
			self.ui.updatePushButton.hide()
			self.ui.addCompoundPushButton.hide()
			self.ui.editLine.hide()
			self.ui.subSearchPushButton.show()
		elif mode == "SubSearch":
			self.ui.subSearchPushButton.show()
			self.ui.searchPushButton.hide()
			self.ui.updatePushButton.hide()
			self.ui.editLine.hide()
			self.ui.searchLine.hide()
			self.ui.addCompoundPushButton.hide()
			self.ui.productsPushButton.hide()
			self.ui.reactantsPushButton.hide()
			self.ui.nameLabel.hide()
			self.ui.formulaLabel.hide()
			self.ui.IUPACLabel.hide()
			self.ui.inchiLabel.hide()
			self.ui.nameLineEdit.hide()
			self.ui.formulaLineEdit.hide()
			self.ui.IUPACLineEdit.hide()
			self.ui.inchiLineEdit.hide()

	def browseStructure(self):
		mol = loadFile(self)
		if mol:
			self.setStructure(mol)

	def showHydrogens(self, toggle):
		if self.compoundItem:
			self.compoundItem.setShowHydrogens(toggle)
		else:
			self.ui.hydrogensPushButton.setChecked(False)

	def validateFormula(self, formula):
		from common import elementNums
		import re

		if not formula:
			return True
		
		elMatch = re.compile("[A-Z][a-z]?") #regex to match elements
		numMatch = re.compile("[0-9]*")
		chargeMatch = re.compile("[+|-][0-3]?|[+|-]*")

		d = {}
		old = ""
		for i in elMatch.finditer(formula):
			el = i.group()
			old += el
			if el not in elementNums.keys():
				return False
			num = numMatch.match(formula, i.end()).group()
			old += num
			if num:
				d[elementNums[el]] = el + num
			else:
				d[elementNums[el]] = el

		charge = chargeMatch.finditer(formula, len(old)).next().group()
		old += charge
		
		if old != formula:
			return ""

		ans = ''
		keys = d.keys()
		keys.sort()
		for i in keys:
			ans += d[i]

		ans += charge
		
		return ans
